package org.jeecg.modules.dock.aspect;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.business.outbound.entity.WmsOutReceiptTask;
import org.jeecg.modules.business.outbound.entity.WmsOutReceiptTaskMaterial;
import org.jeecg.modules.business.outbound.service.IWmsOutReceiptTaskMaterialService;
import org.jeecg.modules.business.outbound.service.IWmsOutReceiptTaskService;
import org.jeecg.modules.business.warehouse.entity.WmsInReceiptTask;
import org.jeecg.modules.business.warehouse.entity.WmsInReceiptTaskMaterial;
import org.jeecg.modules.business.warehouse.service.IWmsInReceiptTaskMaterialService;
import org.jeecg.modules.business.warehouse.service.IWmsInReceiptTaskService;
import org.jeecg.modules.dock.config.JsonUtils;
import org.jeecg.modules.dock.config.WmsConfigPersonnel;
import org.jeecg.modules.dock.constant.SystemConstants;
import org.jeecg.modules.dock.entity.BaseResponseV0;
import org.jeecg.modules.dock.entity.DockDataReceiveRecord;
import org.jeecg.modules.dock.entity.DockTaskRecordMap;
import org.jeecg.modules.dock.entity.dto.response.SapBaseResponseDTO;
import org.jeecg.modules.dock.entity.ems.response.EmsBaseResponseVO;
import org.jeecg.modules.dock.entity.mds.MdsBaseResponseVO;
import org.jeecg.modules.dock.entity.mes.response.MesBaseResponseVO;
import org.jeecg.modules.dock.entity.nest.NestBaseResponseVO;
import org.jeecg.modules.dock.enums.DockDataStatus;
import org.jeecg.modules.dock.mapper.DockTaskRecordMapMapper;
import org.jeecg.modules.dock.service.IDockDataReceiveRecordService;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;


/**
 * @Author scott
 * @email jeecgos@163.com
 * @Date 2018年1月14日
 */
@Aspect
@Component
public class DockLogAspect {

    @Resource
    private IDockDataReceiveRecordService dockDataReceiveRecordService;

    @Resource
    private DockTaskRecordMapMapper dockTaskRecordMapMapper;
    @Resource
    private IWmsInReceiptTaskService iWmsInReceiptTaskService;
    @Resource
    private IWmsInReceiptTaskMaterialService iWmsInReceiptTaskMaterialService;
    @Resource
    private IWmsOutReceiptTaskService iWmsOutReceiptTaskService;
    @Resource
    private IWmsOutReceiptTaskMaterialService iWmsOutReceiptTaskMaterialService;

    @Resource
    private WmsConfigPersonnel personnel;


    @Pointcut("@annotation(org.jeecg.modules.dock.aspect.DockLog)||execution(* org.jeecg.modules.dock.controller.*.*(..))")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        Object result = null;
        if (getDockLog(point) == null ){
            return point.proceed();
        }
        try {
            result = point.proceed();
        } catch (Exception e) {
            result = getErrorResult(point, e.getMessage());
        } finally {
            //保存日志
            saveSysLog(point, result);
        }

        return result;
    }

    private DockLog getDockLog(ProceedingJoinPoint point){
        DockLog re = null;
        Class<?> aClass = point.getTarget().getClass();
        DockLog dockLog = aClass.getAnnotation(DockLog.class);
        re = dockLog;
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DockLog dockLog2 = method.getAnnotation(DockLog.class);
        if (dockLog2 != null){
            re = dockLog2;
        }
        return re;
    }

    private void saveSysLog(ProceedingJoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        DockLog syslog = getDockLog(joinPoint);
        DockDataReceiveRecord receiveLog = getAttach();
        if (receiveLog == null) {
            receiveLog = new DockDataReceiveRecord();
        }
        if (StringUtils.isBlank(receiveLog.getUuid())) {
            receiveLog.setUuid(IdUtil.fastSimpleUUID());
        }
        if (StringUtils.isBlank(receiveLog.getNodeName())) {
            receiveLog.setNodeName("提交");
        }
        receiveLog.setTarget(SystemConstants.WMS);
        receiveLog.setCreateBy(personnel.getByKey(syslog.source()));
        receiveLog.setSource(syslog.source());

        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        receiveLog.setInterfaceName(className + "." + methodName + "()");
        //获取request
        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
        //请求的参数
        String reqestParams = getReqestParams(request, joinPoint, syslog);
        receiveLog.setData(reqestParams);
        receiveLog.setReceiveTime(new Date());
        if (result != null) {
            receiveLog.setResultData(JsonUtils.toJson(result));
        }
        DockDataStatus status = getResult(result);
        receiveLog.setStatus(status);
        receiveLog.setResultCode(status.toString());


        setRecord(syslog.source(), receiveLog, reqestParams);

        dockDataReceiveRecordService.save(receiveLog);
    }


    private DockDataReceiveRecord getAttach() {
        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
        Object att = request.getAttribute(SystemConstants.AttachKey.RECEIVE);
        if (att != null) {
            return (DockDataReceiveRecord) att;
        }

        return null;
    }

    private void setRecord(String source, DockDataReceiveRecord record, String reqParam) {
        if ("SAP".equals(source)) {
            if (reqParam.startsWith("[")) {
                if (JSONUtil.isTypeJSONArray(reqParam)) {
                    JSONArray jsonArray = JSON.parseArray(reqParam);
                    if (ObjectUtil.isNotNull(jsonArray)) {
                        JSONObject jsonObject = JSON.parseObject(jsonArray.get(0).toString());
                        Object ebeln = jsonObject.get("ebeln");
                        if (ObjectUtil.isNotNull(ebeln)) {
                            String documentCode = "";
                            String businessDocument = "";
                            List<WmsInReceiptTaskMaterial> inList = iWmsInReceiptTaskMaterialService.list(new LambdaQueryWrapper<WmsInReceiptTaskMaterial>()
                                    .eq(WmsInReceiptTaskMaterial::getOriginalSystemNumber, ebeln.toString())
                            );
                            List<WmsOutReceiptTaskMaterial> outList = iWmsOutReceiptTaskMaterialService.list(new LambdaQueryWrapper<WmsOutReceiptTaskMaterial>()
                                    .eq(WmsOutReceiptTaskMaterial::getOriginalSystemNumber, ebeln.toString())
                            );
                            if (CollectionUtil.isNotEmpty(inList)) {
                                WmsInReceiptTaskMaterial taskMaterial = inList.get(0);
                                WmsInReceiptTask task = iWmsInReceiptTaskService.getById(taskMaterial.getWmsInReceiptTaskId());
                                if (ObjectUtil.isNotNull(task)) {
                                    documentCode = task.getDocumentCode();
                                    businessDocument = "入库任务";
                                }
                            }
                            if (CollectionUtil.isNotEmpty(outList)) {
                                WmsOutReceiptTaskMaterial taskMaterial = outList.get(0);
                                WmsOutReceiptTask task = iWmsOutReceiptTaskService.getById(taskMaterial.getWmsOutReceiptTaskId());
                                if (ObjectUtil.isNotNull(task)) {
                                    documentCode = task.getDocumentCode();
                                    businessDocument = "出库任务";
                                }
                            }
                            record.setDocumentCode(documentCode);
                            record.setBusinessDocument(businessDocument);
                        }
                    }
                }
            }
        } else {
            String taskCode = null;
            if (JSONUtil.isTypeJSON(reqParam)) {
                if (StrUtil.isNotBlank(reqParam)) {
                    com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(reqParam);
                    taskCode = jsonObject.getString("taskCode");
                    if (StringUtils.isBlank(record.getBusinessCode())) {
                        record.setBusinessCode(jsonObject.getString("taskType"));
                    }

                }
            }
            if (StrUtil.isNotBlank(taskCode)) {
                DockTaskRecordMap recordMap = dockTaskRecordMapMapper.selectOne(new LambdaQueryWrapper<DockTaskRecordMap>()
                        .eq(DockTaskRecordMap::getMesTaskCode, taskCode)
                        .orderByDesc(DockTaskRecordMap::getCreateTime)
                        .last("limit 1")
                );
                if (ObjectUtil.isNotNull(recordMap)) {
                    String documentCode = "";
                    String businessDocument = "";
                    String taskId = recordMap.getTaskId();
                    WmsOutReceiptTask outTask = iWmsOutReceiptTaskService.getById(taskId);
                    WmsInReceiptTask inTask = iWmsInReceiptTaskService.getById(taskId);
                    if (ObjectUtil.isNotNull(inTask)) {
                        documentCode = inTask.getDocumentCode();
                        businessDocument = "入库任务";
                    }
                    if (ObjectUtil.isNotNull(outTask)) {
                        documentCode = outTask.getDocumentCode();
                        businessDocument = "出库任务";
                    }
                    record.setDocumentCode(documentCode);
                    record.setBusinessDocument(businessDocument);
                }
            }
        }


    }


    /**
     * @param request:   request
     * @param joinPoint: joinPoint
     * @Description: 获取请求参数
     * @author: scott
     * @date: 2020/4/16 0:10
     * @Return: java.lang.String
     */
    private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint, DockLog dockLog) {
        String httpMethod = request.getMethod();
        int index = dockLog.index();

        String params = "";
        if (CommonConstant.HTTP_POST.equals(httpMethod) || CommonConstant.HTTP_PUT.equals(httpMethod) || CommonConstant.HTTP_PATCH.equals(httpMethod)) {
            Object[] paramsArray = joinPoint.getArgs();
            // java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
            //  https://my.oschina.net/mengzhang6/blog/2395893
            Object arguments = new Object();
            if (paramsArray != null && paramsArray.length > 0){
                if (paramsArray.length >= index) {
                    arguments = paramsArray[index];
                } else {
                    arguments = paramsArray[0];
                }
            }

            if (arguments instanceof String && JSONUtil.isTypeJSON(arguments.toString())){
                params = arguments.toString();
            }else {
                params = JSON.toJSONString(arguments);
            }

            //update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
        } else {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            // 请求的方法参数值
            Object[] args = joinPoint.getArgs();
            // 请求的方法参数名称
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
            if (args != null && paramNames != null) {
                for (int i = 0; i < args.length; i++) {
                    params += "  " + paramNames[i] + ": " + args[i];
                }
            }
        }
        return params;
    }

    /**
     * @param obj
     * @return
     */
    private DockDataStatus getResult(Object obj) {

        DockDataStatus rest = DockDataStatus.FAILURE;
        if (obj == null) {
            return rest;
        }
        if (obj instanceof BaseResponseV0) {
            BaseResponseV0 res = (BaseResponseV0) obj;
            if (BaseResponseV0.SUCCESS.equals(res.getTarget())) {
                rest = DockDataStatus.SUCCESS;
            }
        } else if (obj instanceof List) {
            List<SapBaseResponseDTO> res = (List<SapBaseResponseDTO>) obj;
            if (CollectionUtil.isNotEmpty(res)) {
                if (SapBaseResponseDTO.SUCCESS.equals(res.get(0).getResult())) {
                    rest = DockDataStatus.SUCCESS;
                }
            }
        } else if (obj instanceof Result) {
            Result res = (Result) obj;
            if (res.isSuccess()) {
                rest = DockDataStatus.SUCCESS;
            }
        }
        return rest;
    }

    private Object getErrorResult(ProceedingJoinPoint point, String msg) {
        DockLog syslog = getDockLog(point);
        String source = syslog.source();
        Object result = null;
        switch (source) {
            case "SAP":
                List<SapBaseResponseDTO> responseDTOS = new ArrayList<>();
                SapBaseResponseDTO dto = SapBaseResponseDTO.buildResponse(false, msg);
                responseDTOS.add(dto);
                result = responseDTOS;
                break;
            case "MES":
                result = MesBaseResponseVO.failure(msg);
                break;
            case "MDS": result = MdsBaseResponseVO.error(msg);
                break;
            case "EMS":
                result = EmsBaseResponseVO.error(msg);
                break;
            case "NEST":
                result = NestBaseResponseVO.failure(msg);
                break;
            default:
                result = Result.error(msg);
        }
        return result;

    }

}
